package com.xlh.virtualization.service.impl;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.core.util.ZipUtil;
import com.google.common.collect.Sets;
import com.xlh.dokka.api.DokkaMagicService;
import com.xlh.enums.LocalResourcesEnum;
import com.xlh.exception.common.GlobalException;
import com.xlh.service.course.ChapterService;
import com.xlh.support.experimentshare.domain.ShareUser;
import com.xlh.support.experimentshare.service.ExperimentShareService;
import com.xlh.util.SshUtil;
import com.xlh.virtualization.dao.helper.ContainerMapperHelper;
import com.xlh.virtualization.domain.Container;
import com.xlh.virtualization.po.ClipboardPO;
import com.xlh.virtualization.po.ResolutionAdjustPO;
import com.xlh.virtualization.service.ExperimentContainerService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

import static com.xlh.virtualization.constant.ContainerConstant.*;
import static com.xlh.virtualization.constant.ContextTypeEnum.COURSE;
import static com.xlh.virtualization.constant.ContextTypeEnum.TASK;

/**
 * @author cheer
 */
@Service
@Slf4j
public class ExperimentContainerServiceImpl implements ExperimentContainerService {

    @Autowired
    private DokkaMagicService dokkaMagicService;

    @Autowired
    private ExperimentShareService experimentShareService;

    @Autowired
    private ChapterService chapterService;

    @Autowired
    private ContainerMapperHelper containerMapperHelper;

    @Override
    public void adjustResolution(ResolutionAdjustPO resolutionAdjustPO) {
        List<Container> containers = containerMapperHelper.listContainers(resolutionAdjustPO.getContainerIds());
        if (CollectionUtils.isEmpty(containers)) {
            throw new GlobalException("虚拟机不存在");
        }

        List<Long> dokkaIds = containers.stream().map(Container::getDokkaId).collect(Collectors.toList());
        List<Long> successDokkaIds = dokkaMagicService.adjustResolution(dokkaIds, resolutionAdjustPO.getLength(),
                resolutionAdjustPO.getWidth());
        if (CollectionUtils.isEmpty(successDokkaIds)) {
            throw new GlobalException("调整分辨率异常");
        }

        Set<Long> successDokkaIdSet = Sets.newHashSet(successDokkaIds);
        List<Long> successIds = containers.stream()
                .filter(container -> successDokkaIdSet.contains(container.getDokkaId()))
                .map(Container::getId)
                .collect(Collectors.toList());
        containerMapperHelper.updateResolution(successIds, resolutionAdjustPO.getFullScreen(),
                resolutionAdjustPO.getLength(), resolutionAdjustPO.getWidth());

        // 通知课程实验分享全屏
        List<ShareUser> shareUsers = containers.stream()
                .filter(container -> successDokkaIdSet.contains(container.getDokkaId()))
                .filter(container -> Objects.equals(container.getContextType(), COURSE.getCode())
                        || Objects.equals(container.getContextType(), TASK.getCode()))
                .map(container ->
                        ShareUser.builder().chapterId(resolutionAdjustPO.getContextId()).userId(container.getUserId()).build())
                .distinct()
                .collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(shareUsers)) {
            experimentShareService.notifyFullScreen(shareUsers, resolutionAdjustPO.getFullScreen());
        }
    }

    @Override
    public String getFtpAddress(Long containerId) {
        Container container = containerMapperHelper.getContainerById(containerId);
        if (container == null) {
            throw new GlobalException("虚拟机不存在");
        }

        String sshExport = dokkaMagicService.getSshExport(container.getDokkaId());
        if (StringUtils.isBlank(sshExport)) {
            throw new GlobalException("查询ftp地址异常");
        }

        return sshExport + "@" + LocalResourcesEnum.SYSTEM_FILE.getMappingUrl();
    }

    @Override
    public void uploadFile(Long containerId, MultipartFile file) {
        Container container = containerMapperHelper.getContainerById(containerId);
        if (container == null) {
            throw new GlobalException("虚拟机不存在");
        }

        String sshExport = dokkaMagicService.getSshExport(container.getDokkaId());
        if (StringUtils.isBlank(sshExport)) {
            throw new GlobalException("文件上传异常");
        }
        String[] hostAndPort = StringUtils.split(sshExport, ':');
        if (hostAndPort.length != 2 || !NumberUtils.isDigits(hostAndPort[1])) {
            log.info("ssh端口异常：{}", sshExport);
            throw new GlobalException("文件上传异常");
        }


        String tmpDir = FILE_UPLOAD_TMP_DIR + containerId;
        FileUtil.mkdir(tmpDir);

        String tmpFile = tmpDir + File.separatorChar + file.getOriginalFilename();
        try {
            file.transferTo(new File(tmpFile));
        } catch (IOException e) {
            log.error("文件上传到服务器异常", e);
            FileUtil.del(tmpDir);
            throw new GlobalException("文件上传异常");
        }

        try (SshUtil sshUtil = SshUtil.newInstance(FILE_UPLOAD_USERNAME, FILE_UPLOAD_PASSWORD, hostAndPort[0],
                Integer.valueOf(hostAndPort[1]))) {
            sshUtil.upload(tmpFile, FILE_UPLOAD_PATH);
        } catch (Exception e) {
            log.error("文件上传到容器异常", e);
            throw new GlobalException("文件上传异常");
        } finally {
            FileUtil.del(tmpDir);
        }
    }

    @Override
    public Boolean clipboard(Long containerId, ClipboardPO clipboardPO, Boolean isStudent) {
        Container container = containerMapperHelper.getContainerById(containerId);
        if (container == null) {
            throw new GlobalException("虚拟机不存在");
        }

        if ((Objects.equals(container.getContextType(), COURSE.getCode()) || Objects.equals(container.getContextType(), TASK.getCode()))
                && isStudent) {
            Integer clipboardSwitch = null;
            try {
                clipboardSwitch = chapterService.getClipboardFalg(null, clipboardPO.getContextId());
            } catch (Exception e) {
                log.error("查询剪切板开关异常", e);
            }
            if (clipboardSwitch != null && clipboardSwitch == 1) {
                throw new GlobalException("剪切板功能被禁止使用");
            }
        }

        return dokkaMagicService.clipboard(container.getDokkaId(), clipboardPO.getContent());
    }

    @Override
    public void zipFile(String filePath) {
        if (!FileUtil.exist(filePath)) {
            FileUtil.mkdir(filePath);
        }
        String zipPath = filePath.substring(0, filePath.length() - 1) + ".zip";
        ZipUtil.zip(filePath, zipPath);
    }

    @Override
    public void downloadFile(String filePath, String fileName) {
        String zipPath = filePath.substring(0, filePath.length() - 1) + ".zip";
        HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
        try (InputStream inputStream = new FileInputStream(zipPath); OutputStream outputStream = response.getOutputStream()) {
            response.setContentType("application/x-download");
            response.addHeader("Content-Disposition", "attachment;filename=" + fileName + ";filename*=UTF-8''" +
                    URLUtil.encode(fileName));
            IOUtils.copy(inputStream, outputStream);
            outputStream.flush();
        } catch (Exception e) {
            log.error("下载文件 {} 异常", zipPath, e);
        } finally {
            FileUtils.deleteQuietly(new File(zipPath));
        }
    }

    @Override
    public String getVncAddress(Long containerId) {
        Container container = containerMapperHelper.getContainerById(containerId);
        if (container == null) {
            throw new GlobalException("虚拟机不存在");
        }

        String vncExport = dokkaMagicService.getExposedPort(container.getDokkaId(), "5901/tcp");
        if (StringUtils.isBlank(vncExport)) {
            throw new GlobalException("查询vnc地址异常");
        }

        return vncExport;
    }

}
